package yao.config;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import yao.config.exception.ConfigException;
import yao.config.exception.ConfigSettingException;


/**
 * XML保存器
 * 
 * @author YaoMing
 */
final class XmlSaver {

	private Storage storage;

	/**
	 * 构造XML保存器
	 * 
	 * @param storage
	 */
	public XmlSaver(Storage storage) {
		this.storage = storage;
	}

	void save() throws ConfigException {
		LinkedList<ConfigPath> configPaths = new LinkedList<ConfigPath>();
		for (Node node : storage.updateMap.values()) {
			configPaths.addAll(node.configPath);
		}

		Document dom = null;
		for (ConfigPath configPath : configPaths) {
			SAXReader sax = new SAXReader();
			try {
				dom = sax.read(configPath.getFile());
			} catch (DocumentException e) {
				throw new ConfigException(e);
			}

			boolean hashChange;
			try {
				hashChange = saveElement(dom.getRootElement(), null);
			} catch (ConfigSettingException e1) {
				throw new ConfigException(e1);
			}
			if (hashChange) {
				OutputFormat format = OutputFormat.createPrettyPrint();
				XMLWriter writer = null;
				try {
					format.setEncoding(dom.getXMLEncoding());
					writer = new XMLWriter(new FileOutputStream(configPath.getFile()), format);
					writer.write(dom);
				} catch (IOException e) {
					throw new ConfigException(e, "Save value error, in \"" + configPath.getPath() + "\"");
				} finally {
					try {
						writer.close();
					} catch (Exception e) {
					}
				}
			}
		}

	}

	private boolean saveElement(Element element, String parentName) throws ConfigSettingException {

		boolean hashChange = false;

		for (Object o : element.elements(PathContants.NODE)) {
			Element e = (Element) o;

			ENode eNode = Element2Node.element2Node(e);

			String fullname = "";
			if (null == parentName) {
				fullname = eNode.getName();
			} else {
				fullname = parentName + "." + e.attributeValue(PathContants.NAME);
			}

			hashChange |= saveElement(e, fullname);

			if (!eNode.isWriteable()) {
				continue;
			}

			Node node = storage.updateMap.get(fullname);
			storage.updateMap.remove(fullname);

			if (null != node) {
				if (null == node.getValue().toString()) {
					if (null != e.element(PathContants.VALUE)) {
						e.remove(e.element(PathContants.VALUE));
					}
					if (null != e.attribute(PathContants.VALUE)) {
						e.remove(e.attribute(PathContants.VALUE));
					}
				} else {
					if (null != eNode.getTextValue()) {
						e.element(PathContants.VALUE).setText(node.getValue().toString());
					} else {
						Attribute att = e.attribute(PathContants.VALUE);
						if (null == att) {
							e.addAttribute(PathContants.VALUE, node.getValue().toString());
						} else {
							att.setValue(node.getValue().toString());
						}
					}
				}
				hashChange = true;
			}

			if (storage.updateMap.size() == 0) {
				break;
			}
		}

		return hashChange;
	}
}
